package st.learnopengl.apps.basic

import glm_.vec4.Vec4
import gln.*
import gln.draw.DrawMode
import gln.draw.glDrawArrays
import gln.glf.VertexAttribute
import gln.identifiers.GlBuffer
import gln.identifiers.GlProgram
import gln.identifiers.GlTexture
import gln.vertexArray.GlVertexArray
import gln.vertexArray.glGenVertexArray
import st.learnopengl.common.App
import st.learnopengl.common.GLUT
import org.lwjgl.opengl.GL11C.*
import org.lwjgl.opengl.GL13.glActiveTexture
import org.lwjgl.opengl.GL13C.GL_TEXTURE0
import org.lwjgl.opengl.GL13C.GL_TEXTURE1
import org.lwjgl.opengl.GL15
import java.nio.ByteBuffer
import java.nio.ByteOrder

class Textures(width: Int, height: Int, title: String) : App(width, height, title) {

    private var program: GlProgram? = null
    private var sample1Location : Int = -1
    private var sample2Location: Int = -1
    private var typeLocation: Int = -1
    private var vao: GlVertexArray? = null
    private var vbo: GlBuffer? = null
    private var texture1: GlTexture? = null
    private var texture2: GlTexture? = null

    private lateinit var vertexAttr: VertexAttribute
    private lateinit var colorAttr: VertexAttribute
    private lateinit var uvAttr: VertexAttribute

    override fun onInit() {
        program = GLUT.createProgram("shaders/textures/vertexShader.glsl"
            , "shaders/textures/fragmentShader.glsl")

        sample1Location = program!!.getUniformLocation("ourTexture1")
        sample2Location = program!!.getUniformLocation("ourTexture2")
        typeLocation = program!!.getUniformLocation("type")

        vao = GlVertexArray(glGenVertexArray())
        vao!!.bound {
            vbo = GlBuffer(GL15.glGenBuffers())
            vbo!!.bound(BufferTarget.ARRAY) {
                val vertexs = listOf<Float>(
                    // positions          // colors           // texture coords
                    0.5f,  0.5f, 0.0f,    1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // top right
                    0.5f, -0.5f, 0.0f,    0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // bottom right
                    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // bottom left
                    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // top left
                )
                val bb = ByteBuffer.allocateDirect(vertexs.size * 4)
                bb.order(ByteOrder.nativeOrder())
                val fb = bb.asFloatBuffer()
                vertexs.forEach {
                    fb.put(it)
                }
                fb.position(0)
                data(fb, Usage.STATIC_DRAW)
            }
        }

        vertexAttr = VertexAttribute(0, 3, VertexAttrType.FLOAT, false, 8 * 4, 0)
        colorAttr = VertexAttribute(1, 3, VertexAttrType.FLOAT, false, 8 * 4, 3 * 4)
        uvAttr = VertexAttribute(2, 2, VertexAttrType.FLOAT, false, 8 * 4, 6 * 4)

        texture1 = GLUT.loadTexture("textures/brickwall.jpg")
        texture2 = GLUT.loadTexture("textures/awesomeface.png")


        glClearColor(Vec4(0.3, 0.4, 0.5, 1.0))
    }

    override fun loop() {
        glViewport(0, 0, width, height)
        glClear(GL_COLOR_BUFFER_BIT)

        glViewport(0, 0, width / 2, height / 2)
        program!!.use()

        vao!!.bind()
        vbo!!.bind(BufferTarget.ARRAY)

        vertexAttr.enable()
        vertexAttr.pointer()

        uvAttr.enable()
        uvAttr.pointer()

        colorAttr.enable()
        colorAttr.pointer()

        texture1!!.bind(TextureTarget._2D)
        glActiveTexture(GL_TEXTURE1)

        program!!.programUniform(sample1Location, 1)
        program!!.programUniform(typeLocation, 0)

        glDrawArrays(DrawMode.TRIANGLE_FAN, 4)

        vertexAttr.disable()
        uvAttr.disable()
        colorAttr.disable()

        glViewport(width / 2, 0, width / 2, height / 2)

        program!!.use()

        vao!!.bind()
        vbo!!.bind(BufferTarget.ARRAY)

        vertexAttr.enable()
        vertexAttr.pointer()

        uvAttr.enable()
        uvAttr.pointer()

        colorAttr.enable()
        colorAttr.pointer()

        texture1!!.bind(TextureTarget._2D)
        glActiveTexture(GL_TEXTURE1)

        program!!.programUniform(sample1Location, 1)
        program!!.programUniform(typeLocation, 1)

        glDrawArrays(DrawMode.TRIANGLE_FAN, 4)

        vertexAttr.disable()
        uvAttr.disable()
        colorAttr.disable()

        glViewport(0, height / 2, width / 2, height / 2)

        program!!.use()

        vao!!.bind()
        vbo!!.bind(BufferTarget.ARRAY)

        vertexAttr.enable()
        vertexAttr.pointer()

        uvAttr.enable()
        uvAttr.pointer()

        colorAttr.enable()
        colorAttr.pointer()

        texture1!!.bind(TextureTarget._2D)
        glActiveTexture(GL_TEXTURE1)

        program!!.programUniform(sample1Location, 1)
        program!!.programUniform(typeLocation, 2)

        glDrawArrays(DrawMode.TRIANGLE_FAN, 4)

        vertexAttr.disable()
        uvAttr.disable()
        colorAttr.disable()

        glViewport(width / 2, height / 2, width / 2, height / 2)

        program!!.use()

        vao!!.bind()
        vbo!!.bind(BufferTarget.ARRAY)

        vertexAttr.enable()
        vertexAttr.pointer()

        uvAttr.enable()
        uvAttr.pointer()

        colorAttr.enable()
        colorAttr.pointer()

        texture1!!.bind(TextureTarget._2D)
        glActiveTexture(GL_TEXTURE1)

        texture2!!.bind(TextureTarget._2D)
        glActiveTexture(GL_TEXTURE0)

        program!!.programUniform(sample1Location, 1)
        program!!.programUniform(sample2Location, 0)
        program!!.programUniform(typeLocation, 3)

        glDrawArrays(DrawMode.TRIANGLE_FAN, 4)

        vertexAttr.disable()
        uvAttr.disable()
        colorAttr.disable()
    }

    override fun cleanUp() {
        super.cleanUp()

        texture1!!.delete()
        texture2!!.delete()

        vbo!!.delete()
        vao!!.delete()
        program!!.delete()
    }


    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            Textures(1280, 720, "Textures").run()
        }
    }
}